﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SilverTile.Xaml;
using System.Collections.Generic;
using SilverTile.Sprites;
using System.Linq;

namespace SilverTileTests
{
    [TestClass]
    public class XamlAreaTransitionTests
    {

        SilverTileGame _game;
        ISprite _player;
        GameAreaGrid _area1;
        GameAreaGrid _area2;

        [TestInitialize]
        public void Setup_Game_With_Area_Transition()
        {
            var tileSource = new SpriteFrame { Source = "/SilverTileTests;component/Tiles/0002.png" };

            _area1 = new GameAreaGrid
            {
                TileWidth = 32,
                TileHeight = 32,
                Columns = 2,
                Rows = 1,
                AreaName = "Start"
            };

            _area1.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 0, Frame = tileSource });
            var tileWithTransition = new SpriteTile
            {
                X = 1,
                Y = 0,
                Transition = new AreaTransition
                {
                    GameAreaName = "Target",
                    TargetColumn = 1,
                    TargetRow = 0,
                    CollisionSides = SilverTile.Collision.Left
                },
                Frame = tileSource
            };
            _area1.BackgroundSprites.Add(tileWithTransition);

            _area2 = new GameAreaGrid
            {
                TileWidth = 32,
                TileHeight = 32,
                Columns = 2,
                Rows = 1,
                AreaName = "Target"
            };
            _area2.BackgroundSprites.Add(new SpriteTile { X = 0, Y = 0, Frame = tileSource });
            _area2.BackgroundSprites.Add(new SpriteTile { X = 1, Y = 0, Frame = tileSource });

            var gameWorld = new SilverTileGameWorld
            {
                GameAreas = new List<GameAreaGrid> { _area1, _area2 },
                StartAreaName = "Start",
                StartColumn = 0,
                StartRow = 0,
                Viewport = new SilverTile.Viewports.Viewport(64, 32)
            };

            _player =
                new SpriteAnimation
                {
                    Animations = new List<FrameAnimation>
                    {
                        new FrameAnimation
                        {
                            Frames = new List<SpriteFrame> 
                            {
                                tileSource
                            }
                        }
                    },
                    X = 0,
                    Y = 0
                }.GetAnimatedSprite<AnimatedSprite>();

            _game = new SilverTileGame
            {
                Width = 64,
                Height = 32,
                GameWorld = gameWorld,
                Running = true
            };

            gameWorld.PlayerSprite = _player;

        }

        [TestMethod]
        public void AreaTransition_Should_Have_TargetRow_TargetColumn_And_GameAreaName_Properties()
        {


            var areaTransition = new AreaTransition
            {
                TargetColumn = 1,
                TargetRow = 2,
                GameAreaName = "name",
                CollisionSides = SilverTile.Collision.Top
            };

            Assert.AreEqual(1, areaTransition.TargetColumn, "TargetColumn was not set properly");
            Assert.AreEqual(2, areaTransition.TargetRow, "TargetRow was not set properly");
            Assert.AreEqual("name", areaTransition.GameAreaName, "GameAreaName was not set properly");
            Assert.AreEqual(SilverTile.Collision.Top, areaTransition.CollisionSides, "CollisionSides was not set properly");

            Assert.AreEqual(areaTransition.TargetColumn, areaTransition.GetValue(AreaTransition.TargetColumnProperty),
                "TargetColumn and TargetColumnProperty does not reutrn the same value");
            Assert.AreEqual(areaTransition.TargetRow, areaTransition.GetValue(AreaTransition.TargetRowProperty),
                "TargetRow and TargetRowProperty does not reutrn the same value");
            Assert.AreEqual(areaTransition.GameAreaName, areaTransition.GetValue(AreaTransition.GameAreaNameProperty),
                "GameAreaName and GameAreaNameProperty does not reutrn the same value");
            Assert.AreEqual(areaTransition.CollisionSides, areaTransition.GetValue(AreaTransition.CollisionSidesProperty),
                "CollisionSides and CollisionSideProperty does not return the same value");

        }

        [TestMethod]
        public void Entering_A_Tile_With_AreaTransition_Should_Change_The_Active_Area()
        {
            
            _game.Update(TimeSpan.FromMilliseconds(100));

            Assert.AreSame(_area1, _game.GameWorld.ActiveArea, "ActiveArea is not the start area");

            // Move the player into the area transition
            _player.X = 25;

            _game.Update(TimeSpan.FromMilliseconds(100)); // This should trigger the transition

            Assert.AreSame(_area2, _game.GameWorld.ActiveArea, "ActiveArea was not changed to area 2");

        }

        [TestMethod]
        public void Entering_Area_Transition_From_The_Wrong_Side_Should_Not_Cause_A_Transition()
        {                   

            // Move the player into the area transition, but at the right side
            _player.X = 60;

            _game.Update(TimeSpan.FromMilliseconds(100));
            Assert.AreSame(_area1, _game.GameWorld.ActiveArea, "ActiveArea is not the start area");

            // Move the player into the left side of the tile, this should cause the transition to trigger
            _player.X = 25;

            _game.Update(TimeSpan.FromMilliseconds(100));
            Assert.AreNotSame(_area1, _game.GameWorld.ActiveArea, "ActiveArea is still the start area");

        }

        [TestMethod]
        public void AreaTransition_Should_Remove_All_Active_Sprites_Except_Player()
        {

            ISprite monster = new Sprite(10, 10);
            _game.Viewport.AddSprite("monster", monster);

            _player.X = 25;

            _game.Update(TimeSpan.FromMilliseconds(100));
            
            Assert.AreEqual(_game.Viewport.ActiveSprites.Count, 1, "There is more than one active sprite in the viewport");

            Assert.IsTrue(_game.Viewport.ActiveSprites.Values.Contains(_player), "Player is not in the list of active sprites");

        }

        [TestMethod]
        public void Area_transition_in_target_area_should_not_be_activated_until_player_has_left_it()
        {

            // Add an area transition at the target for the first area transition
            var target = _area2.BackgroundSprites.Last();
            var targetTransition = new AreaTransition
            {
                GameAreaName = "Start",
                TargetColumn = 1,
                TargetRow = 0,
                CollisionSides = SilverTile.Collision.All
            };
            target.Transition = targetTransition;

            // Make sure both area transitions trigger as soon as the player touches them
            _area1.BackgroundSprites.Where(s => s.Transition != null).First().Transition.CollisionSides = SilverTile.Collision.All;

            // This should invoke an area transition
            _player.X = 25;
            _game.Update(TimeSpan.FromMilliseconds(100));
            
            Assert.AreNotSame(_area1, _game.GameWorld.ActiveArea, "Start area is still the active area");

            // Move in place to transition back
            _player.X = 25;

            // But now we don't want to transition back
            _game.Update(TimeSpan.FromMilliseconds(100));

            Assert.AreNotSame(_area1, _game.GameWorld.ActiveArea, "Start area is the active area again");

            // Move the player out of the area transitions area
            _player.X = 0;
           
            // There should still be no area transition
            _game.Update(TimeSpan.FromMilliseconds(100));

            Assert.AreNotSame(_area1, _game.GameWorld.ActiveArea, "ActiveArea is still the active area");

            // Move the player back into the area transitions area
            _player.X = 25;
            _game.Update(TimeSpan.FromMilliseconds(100));
            
            // We should be back at area 1
            Assert.AreSame(_area1, _game.GameWorld.ActiveArea, "ActiveArea is not the active area");

        }

    }
}